#include <hw/interrupts.h>
#include <utils/debug.h>
#include <utils/io.h>

interrupt_handler irq_handlers[16];

void irq_handler(struct interrupt_info info) {
    if(irq_handlers[info.int_no - 32] != 0){
        interrupt_handler handler = irq_handlers[info.int_no - 32];
        handler(info);
    }

    if(info.int_no >= 40){
        // send EOI to slave
        outb(0xA0, 0x20);
    }
    // send EOI to master
    outb(0x20, 0x20);
}

char* exceptions[] = {
    "Division By Zero",
    "Debug",
    "Non Maskable Interrupt",
    "Breakpoint",
    "Into Detected Overflow",
    "Out of Bounds",
    "Invalid Opcode",
    "No Coprocessor",
    "Double Fault",
    "Coprocessor Segment Overrun",
    "Bad TSS",
    "Segment Not Present",
    "Stack Fault",
    "General Protection Fault",
    "Page Fault",
    "Unknown Interrupt",
    "Coprocessor Fault",
    "Alignment Check",
    "Machine Check"
};

void isr_handler(struct interrupt_info info) {
    if(info.int_no == 14) {
        // Page fault have more info
        uint32 faulting_address;
        asm volatile("mov %%cr2, %0" : "=r" (faulting_address));

        int not_present = info.err_code & 0x1;     // Page not present
        int write       = info.err_code & 0x2;     // Write operation
        int user        = info.err_code & 0x4;     // User mode
        // UNUSED int reserved    = info.err_code & 0x8;     // CPU-reserved bits were overwritten
        //int fetch       = info.err_code & 0x10;    // Fetch
        kerror("\nPANIC: Page fault: trying to %s a %s %s page at address 0x%x\n", 
            write ? "write" : "read",
            not_present ? "not present" : "present",
            user ? "user" : "kernel",
            faulting_address);
        return; // already handled
    }

    if(info.int_no < 19) {
        kerror("%s Exception. System Halted!\n", exceptions[info.int_no]);
    } else if(info.int_no < 31) {
        kerror("Unknown 0x%x Exception. System Halted!\n", info.int_no);
    }
}

void register_irq(uint8 int_no, interrupt_handler handler) {
    irq_handlers[int_no] = handler;
}

